package com.gitee.huanminabc.utils_common.base;


import com.gitee.huanminabc.jcommon.reflect.FieldUtil;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.*;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.*;
import java.util.regex.Pattern;

/**
 * xml工具
 */
//https://www.w3school.com.cn/xpath/xpath_axes.asp
// 获取xml节点和属性的value
public class XmlUtil {
    // 将xml字符串转换为Document对象
    public static Document parseXmlString(String xmlString) {
        Document document = null;
        try {
            document = DocumentHelper.parseText(xmlString);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
        return document;
    }
    
    //将Document对象转换为xml字符串
    public static String parseXmlString(Document document) {
        return document.asXML();
    }
    //将xml字符串转换为Map对象 ,只支持一层
    public static Map<String, String> getXmlToMap(String xml,String rootName ,String tabName,String attrName) {
        Map<String, String> map = new HashMap<>();
        Document document = parseXmlString(xml);
        Node root = document.selectSingleNode("//" + rootName);
        List<Node> nodes = root.selectNodes("//"+tabName);
        for (Node node : nodes) {
            Node arrtN = node.selectSingleNode("@" + attrName);
            String arrtV = arrtN.getText();
            map.put(arrtV,node.getText());
        }
        return map;
    }
    //将xml字符串转换为List<Map>对象 ,平级支持多层
    public static List<Map<String,String>> getXmlToMaps(String xml,String rootNames ,String tabName,String attrName) {
        List<Map<String,String>> list = new ArrayList<>();
        Document document = parseXmlString(xml);
        List<Node> roots = document.selectNodes("//" + rootNames);
        for (Node root : roots) {
            Map<String,String> map = new HashMap<>();
            List<Node> nodes = root.selectNodes("child::"+tabName);
            for (Node node : nodes) {
                Node arrtN = node.selectSingleNode("@" + attrName);
                String arrtV = arrtN.getText();
                map.put(arrtV,node.getText());
            }
            list.add(map);
        }
        return list;
    }
    
    
    //将Map对象转换为xml字符串
    public static String getMapToXml(Map<String, String> map, String rootName, String tabName, String attrName) {
        Document document = DocumentHelper.createDocument();
        Element root = document.addElement(rootName);
        for (Map.Entry<String, String> entry : map.entrySet()) {
            Element tab = root.addElement(tabName);
            tab.addAttribute(attrName, entry.getKey());
            tab.setText(entry.getValue());
        }
        return parseXmlString(document);
    }

   
    //将bean对象转换为xml字符串
    public static String getBeanToXml(Object bean, String rootName, String tabName, String attrName)  {
        Document document = DocumentHelper.createDocument();
        try {
            Class<?> aClass = bean.getClass();
            Element root = document.addElement(rootName);
            Field[] fields = FieldUtil.getFields(aClass);//获取所有属性
            for (Field field : fields) {
                Element tab = root.addElement(tabName);
                tab.addAttribute(attrName, field.getName());
                Object v = field.get(bean);
                tab.setText(v == null ? "" : v.toString());
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return parseXmlString(document);
    }
    
  
    //将xml字符串转换为bean对象 ,只支持一层,并且属性名和xml属性值一致,Bean类型必须都是String 否则报错 java.lang.IllegalArgumentException: Can not set int field
    public static <T> T getXmlToBean(String xml, String rootName, String tabName, String attrName, Class<T> clazz) {
        T t = null;
        try {
            t = clazz.newInstance();
            Document document = parseXmlString(xml);
            Node root = document.selectSingleNode("//" + rootName);
            List<Node> nodes = root.selectNodes("//"+tabName);
            for (Node node : nodes) {
                Node arrtN = node.selectSingleNode("@" + attrName);
                String arrtV = arrtN.getText();
                Field field = FieldUtil.getField(clazz, arrtV);
                if (field != null) {
                    field.setAccessible(true);
                    field.set(t, node.getText());
                }
            }
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return t;
    }
    
    // 获取xml指定tabName 的第一个值
    public static String getXmlByTabNameOne(String xmlString, String tabName) {
        Document document = parseXmlString(xmlString);
        Node node = document.selectSingleNode("//" + tabName);
        return node == null ? null : node.getText();
    }
    // 获取xml指定tabName 的所有值
    public static List<String> getXmlByTabName(String xmlString, String tabName) {
        Document document = parseXmlString(xmlString);
        List<Node> nodes = document.selectNodes("//" + tabName);
        List<String> list = new ArrayList<>();
        for (Node node : nodes) {
            list.add(node.getText());
        }
        return list;
    }
    // 获取xml指定attrName的第一个值
    public static String getXmlByAttrNameOne(String xmlString, String attrName) {
        Document document = parseXmlString(xmlString);
        Node node = document.selectSingleNode("//@" + attrName);
        return node == null ? null : node.getText();
    }
    // 获取xml指定attrName的所有值
    public static List<String> getXmlByAttrName(String xmlString, String attrName) {
        Document document = parseXmlString(xmlString);
        List<Node> nodes = document.selectNodes("//@" + attrName);
        List<String> list = new ArrayList<>();
        for (Node node : nodes) {
            list.add(node.getText());
        }
        return list;
    }

    // 获取xml指定tabName , attrName=attrValue
    public static String getXmlByTabNameAndAttrNameEqAttrValueOne(String xmlString, String tabName, String attrName, String attrValue) {
        Document document = parseXmlString(xmlString);
        Node node = document.selectSingleNode("//" + tabName + "[@" + attrName + "='" + attrValue + "']");
        return node == null ? null : node.getText();
    }
    // 获取xml指定tabName , attrName=attrValue
    public static List<String> getXmlByTabNameAndAttrNameEqAttrValueAll(String xmlString, String tabName, String attrName, String attrValue) {
        Document document = parseXmlString(xmlString);
        List<Node> nodes = document.selectNodes("//" + tabName + "[@" + attrName + "='" + attrValue + "']");
        List<String> list = new ArrayList<>();
        for (Node node : nodes) {
            list.add(node.getText());
        }
        return list;
    }
   //获取指定tabName包含attrName的第一个tabName值
   public static List<String> getXmlByTabNameAndAttrNameRetTabNameOne(String xmlString, String tabName, String attrName) {
        Document document = parseXmlString(xmlString);
        List<Node> nodes = document.selectNodes("//" + tabName + "[@" + attrName + "]");
        List<String> list = new ArrayList<>();
        for (Node node : nodes) {
            list.add(node.getText());
        }
        return list;
    }
    //获取指定tabName包含attrName的所有tabName值
    public static List<String> getXmlByTabNameAndAttrNameRetTabNameAll(String xmlString, String tabName, String attrName) {
        Document document = parseXmlString(xmlString);
        List<Node> nodes = document.selectNodes("//" + tabName + "[@" + attrName + "]");
        List<String> list = new ArrayList<>();
        for (Node node : nodes) {
            list.add(node.getText());
        }
        return list;
    }
    //获取指定tabName包含attrName的第一个attrName值
    public static List<String> getXmlByTabNameAndAttrNameRetAttrValueOne(String xmlString, String tabName, String attrName) {
        Document document = parseXmlString(xmlString);
        List<Node> nodes = document.selectNodes("//" + tabName + "[@" + attrName + "]");
        List<String> list = new ArrayList<>();
        for (Node node : nodes) {
            Node arrtN = node.selectSingleNode("@" + attrName);
            list.add(arrtN.getText());
        }
        return list;
    }
    //获取指定tabName包含attrName的所有attrName值
    public static List<String> getXmlByTabNameAndAttrNameRetAttrValueAll(String xmlString, String tabName, String attrName) {
        Document document = parseXmlString(xmlString);
        List<Node> nodes = document.selectNodes("//" + tabName + "[@" + attrName + "]");
        List<String> list = new ArrayList<>();
        for (Node node : nodes) {
            Node arrtN = node.selectSingleNode("@" + attrName);
            list.add(arrtN.getText());
        }
        return list;
    }


    @Slf4j
    public static final class LocalUtil {


        private static final String ANYHOST_VALUE = "0.0.0.0";
        private static final String LOCALHOST_VALUE = "127.0.0.1";
        private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");


        /**
         * 本地地址 缓存使用
         */
        private static volatile InetAddress LOCAL_ADDRESS = null;

        /**
         * 从本地网卡找到第一个有效IP
         *
         * @return first valid local IP
         */
        public static InetAddress getLocalAddress() {
            if (LOCAL_ADDRESS != null) {
                return LOCAL_ADDRESS;
            }
            InetAddress localAddress = getLocalAddress0();
            LOCAL_ADDRESS = localAddress;
            return localAddress;
        }

        /**
         *  获取本地ip
         *
         * @return String
         */
        public static String getIp(){
            return getLocalAddress().getHostAddress();
        }

        /**
         * get ip:port
         *
         * @param port
         * @return String
         */
        public static String getIpPort(int port){
            String ip = getIp();
            return getIpPort(ip, port);
        }
        /**
         * get [http|https]://ip:port
         *
         * @param port
         * @return String
         */
        public static String getProtocolIpPort(String protocol,int port){
            String ip = getIp();
            String ipPort = getIpPort(ip, port);
            return protocol.concat("://").concat(ipPort);
        }

        public static String getIpPort(String ip, int port){
            if (ip == null) {
                return null;
            }
            return ip.concat(":").concat(String.valueOf(port));
        }

        //将ip:port解析成数组
        public static Object[] parseIpPort(String address){
            String[] array = address.split(":");

            String host = array[0];
            int port = Integer.parseInt(array[1]);

            return new Object[]{host, port};
        }

        //获取当前应用的端口
        public static int getPort(){
           return Integer.parseInt(System.getProperty("server.port"));
        }

        //获取当前应用的ip:port
        public static String getIpPort(){
            return getIpPort(getPort());
        }


        // ---------------------- valid ----------------------

        private static InetAddress toValidAddress(InetAddress address) {
            if (address instanceof Inet6Address) {
                Inet6Address v6Address = (Inet6Address) address;
                if (isPreferIPV6Address()) {
                    return normalizeV6Address(v6Address);
                }
            }
            if (isValidV4Address(address)) {
                return address;
            }
            return null;
        }

        private static boolean isPreferIPV6Address() {
            return Boolean.getBoolean("java.net.preferIPv6Addresses");
        }

        /**
         * valid Inet4Address
         *
         * @param address address
         * @return ture
         */
        private static boolean isValidV4Address(InetAddress address) {
            if (address == null || address.isLoopbackAddress()) {
                return false;
            }
            String name = address.getHostAddress();
            return (name != null
                    && IP_PATTERN.matcher(name).matches()
                    && !ANYHOST_VALUE.equals(name)
                    && !LOCALHOST_VALUE.equals(name));
        }


        /**
         * normalize the ipv6 Address, convert scope name to scope id.
         * e.g.
         * convert
         * fe80:0:0:0:894:aeec:f37d:23e1%en0
         * to
         * fe80:0:0:0:894:aeec:f37d:23e1%5
         * <p>
         * The %5 after ipv6 address is called scope id.
         * see java doc of {@link Inet6Address} for more details.
         *
         * @param address the input address
         * @return the normalized address, with scope id converted to int
         */
        private static InetAddress normalizeV6Address(Inet6Address address) {
            String addr = address.getHostAddress();
            int i = addr.lastIndexOf('%');
            if (i > 0) {
                try {
                    return InetAddress.getByName(addr.substring(0, i) + '%' + address.getScopeId());
                } catch (UnknownHostException e) {
                    // ignore
                    log.debug("Unknown IPV6 address: ", e);
                }
            }
            return address;
        }

        // ---------------------- find ip ----------------------


        private static InetAddress getLocalAddress0() {
            InetAddress localAddress = null;
            try {
                localAddress = InetAddress.getLocalHost();
                InetAddress addressItem = toValidAddress(localAddress);
                if (addressItem != null) {
                    return addressItem;
                }
            } catch (Throwable e) {
                log.error(e.getMessage(), e);
            }

            try {
                Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
                while (interfaces.hasMoreElements()) {
                    try {
                        NetworkInterface network = interfaces.nextElement();
                        if (network.isLoopback() || network.isVirtual() || !network.isUp()) {
                            continue;
                        }
                        Enumeration<InetAddress> addresses = network.getInetAddresses();
                        while (addresses.hasMoreElements()) {
                            try {
                                InetAddress addressItem = toValidAddress(addresses.nextElement());
                                if (addressItem != null) {
                                    try {
                                        if(addressItem.isReachable(100)){
                                            return addressItem;
                                        }
                                    } catch (IOException e) {
                                        // ignore
                                    }
                                }
                            } catch (Throwable e) {
                                log.error(e.getMessage(), e);
                            }
                        }
                    } catch (Throwable e) {
                        log.error(e.getMessage(), e);
                    }
                }
            } catch (Throwable e) {
                log.error(e.getMessage(), e);
            }
            return localAddress;
        }

        public static void main(String[] args) {
            String ipPort = LocalUtil.getIpPort(80);
            System.out.println(ipPort);


        }
    }
}